#pragma once
// 定长内存池
// 适用于只申请一种类型的空间，并且进程很快就释放
#include "Common.hpp"

using std::cout;
using std::endl;

template<class T>
class FLMemoryPool
{
public:
	FLMemoryPool() = default;
	FLMemoryPool(const FLMemoryPool&) = delete;
	FLMemoryPool operator=(const FLMemoryPool&) = delete;

	T* New()  // 申请该类型的内存块
	{
		T* obj = nullptr;
		// 先查看自由链表 - 即回收的内存块是否还有
		if (_freeList)
		{
			void* next = NextObj(_freeList);
			obj = (T*)_freeList;
			_freeList = next;  // 注意，这里可能剩下一点内存碎片我们是不需要的
		}
		else
		{
			// 如果我们的自由链表没有内存块了，在到大内存块中去分
			if (_surplus < sizeof(T))
			{
				// 此时该申请新的一大块内存了
				_surplus = 128 * 1024;  // 128kb
				//_memory = (char*)malloc(_surplus);
				_memory = (char*)SystemAlloc(_surplus >> 13);  // 128k / 8 128*1024 / (1024 * 2 ^ 3) 除法向低位进行移动
				if (_memory == nullptr) throw std::bad_alloc();  // 抛异常
			}

			obj = (T*)_memory;  // 切分
			size_t dp = sizeof(T) > sizeof(void*) ? sizeof(T) : sizeof(void*);  // 每个内存块分好对应存指针的空间，最少也是当前环境下的指针大小(适用于x86和x64)
			_memory += dp;
			_surplus -= dp;
		}

		new(obj)T;  // 定位new 调用其类型的默认构造函数进行初始化  https://blog.csdn.net/shujh_sysu/article/details/51925426  new(对应分配空间)类型(参数)// 没有参数可以省略 此处要求只能使用无参
		return obj;
	}

	void Delete(T* obj)  // 释放对应对象的内存块
	{
		obj->~T();  // 调用申请类型的析构函数
		// 头插，先让插入元素的指针指向后面第一个地址
		*(void**)obj = _freeList;
		_freeList = obj;
	}

private:
	char* _memory = nullptr;	// 先向系统申请一大块内存，自己管理
	size_t _surplus = 0;		// 剩余空间字节数
	void* _freeList = nullptr;  // 自由链表，存储回收的固定内存块
};

// 测试代码
//struct TreeNode
//{
//	int _val;
//	TreeNode* _left;
//	TreeNode* _right;
//
//	TreeNode()
//		:_val(0)
//		, _left(nullptr)
//		, _right(nullptr)
//	{}
//};
//
//void TestFLMemoryPool()
//{
//	// 申请释放的轮次
//	const size_t Rounds = 5;
//
//	// 每轮申请释放多少次
//	const size_t N = 1000000;
//
//	std::vector<TreeNode*> v1;
//	v1.reserve(N);
//
//	size_t begin1 = clock();
//	for (size_t j = 0; j < Rounds; ++j)
//	{
//		for (int i = 0; i < N; ++i)
//		{
//			v1.push_back(new TreeNode);
//		}
//		for (int i = 0; i < N; ++i)
//		{
//			delete v1[i];
//		}
//		v1.clear();
//	}
//
//	size_t end1 = clock();
//
//	std::vector<TreeNode*> v2;
//	v2.reserve(N);
//
//	FLMemoryPool<TreeNode> TNPool;
//	size_t begin2 = clock();
//	for (size_t j = 0; j < Rounds; ++j)
//	{
//		for (int i = 0; i < N; ++i)
//		{
//			v2.push_back(TNPool.New());
//		}
//		for (int i = 0; i < N; ++i)
//		{
//			TNPool.Delete(v2[i]);
//		}
//		v2.clear();
//	}
//	size_t end2 = clock();
//
//	cout << "new cost time:" << end1 - begin1 << endl;
//	cout << "object pool cost time:" << end2 - begin2 << endl;
//}